import { doRef, useCreateCall, useSimpleMemo } from '@/components';
import React, {
  Children,
  cloneElement,
  createContext,
  createRef,
  isValidElement,
  useContext,
  useEffect,
  useLayoutEffect,
  useState,
} from 'react';
import { findDOMNode } from 'react-dom';

const HoverContext = createContext(false);

function Hover({ children }) {
  const [hover, setHover] = useState(false);
  const [refList, setRefList] = useState([]);
  const createCall = useCreateCall();
  const childCount = Children.count(children);
  const [renderChildren, setChildren] = useState(null);

  useEffect(() => {
    if (refList.length > childCount) {
      setRefList(refList.slice(0, childCount));
    }
    if (refList.length < childCount) {
      const addSize = childCount - refList.length;
      const concatRefs = new Array(addSize).fill(1).map(() => createRef());
      setRefList(refList.concat(concatRefs));
    }
  }, [childCount]);

  useEffect(() => {
    if (childCount === refList.length) {
      setChildren(
        Children.map(children, (child, index) =>
          isValidElement(child)
            ? cloneElement(child, {
                ref: createCall.of(index, (instance) => {
                  doRef(instance, child?.props?.ref);
                  doRef(instance, refList[index]);
                }),
              })
            : child,
        ),
      );
    }
  }, [children, refList]);

  useLayoutEffect(() => {
    let elements = [];
    let outTimer;
    const onMouseEnter = () => {
      clearTimeout(outTimer);
      setHover(true);
    };
    const onMouseOut = () => {
      clearTimeout(outTimer);
      outTimer = setTimeout(() => {
        setHover(false);
      }, 100);
    };

    refList.forEach((ref) => {
      let el = null;
      if (ref.current) el = findDOMNode(ref.current);
      if (el) elements.push(el);
    });

    elements.forEach((el) => {
      el.addEventListener('mouseenter', onMouseEnter);
      el.addEventListener('mouseover', onMouseEnter);
      el.addEventListener('mouseout', onMouseOut);
      el.addEventListener('mouseleave', onMouseOut);
    });

    return () => {
      clearTimeout(outTimer);
      elements.forEach((el) => {
        el.removeEventListener('mouseenter', onMouseEnter);
        el.removeEventListener('mouseover', onMouseEnter);
        el.removeEventListener('mouseout', onMouseOut);
        el.removeEventListener('mouseleave', onMouseOut);
      });
    };
  }, [renderChildren]);

  return <HoverContext.Provider value={hover} children={renderChildren} />;
}

export function HoverStyle({ style, hoverStyle, children, ...props }) {
  const hover = useHover();
  const computedStyle = useSimpleMemo({
    transition: `${Object.keys(hoverStyle)} .2s ease-out`,
    ...style,
    ...(hover ? hoverStyle : null),
  });

  return Children.map(children, (t) => cloneElement(t, { style: computedStyle, ...props }));
}

export function useHover() {
  return useContext(HoverContext);
}

export default Hover;
